from . import api
from ihome.utils.captcha.captcha import captcha # 导入图片验证码模块
from ihome import redis_store,db # 导入操作redis数据库的实例对象
from ihome import constance
from ihome.utils.response_code import RET
from flask import make_response,jsonify,current_app
from ihome.models import User
import random
# from ihome.libs.yuntongxun.sms import CPP

# from ihome.tasks.task_sms import send_sms # 导入使用异步发送短信的函数

from ihome.tasks.sms.tasks import send_sms # 导入使用异步发送短信的函数

# GET http://127.0.0.1:5000/api/v1.0/image_codes/<image_code_id>
@api.route("/image_codes/<image_code_id>")
def get_image_code(image_code_id):
    """
    获取图片验证码
    :param image_code_id:用户传入的图片验证码编号
    :return:验证码图片
    """
    # 后端处理逻辑的固定套路
    # 1. 接收客户端传递过来的参数，当前采用固定URL传递参数，必须传递，否则无法访问成功
    # 2. 验证参数:一般校验是否传递了参数，当前必传
    # 3.执行业务逻辑
    # 3.1 生成验证码图片
    #名称 内容 图片数据
    name,text,image_data = captcha.generate_captcha()
    # 3.2 将验证码图片和编号存储到redis数据库中，格式：image_code_编号：图片验证码内容
    # A: redis数据库中数据类型：string、 list、 set、 sorted set、hash
    # B:采用hash存储图片验证码的过程分析： hset image_code id  content
    # hset image_code 1 abc
    #                 2 def
    # 当让编号为1的abc过期，则当前所有记录都过期，不满足要求，pass掉
    # 使用list存储： image_code:["编号：内容","编号:内容"] ，每次使用前需要拆分数据，不方便
    # 使用string存储：set image_code_编号：图片验证码内容
    # set image_code_1 abc
    # set image_code_2 def
    # expire image_code_1 1
    # get image_code_1 为空，说明过期了
    # get image_code_2 值为def，正常，
    # 经过分析，这种方式最合适，建议采用
    # redis_store.set("image_code_%s"%image_code_id,text)
    # # 有效期不建议写死
    # redis_store.expire("image_code_%s"%image_code_id,constance.IMAGE_CODE_REDIS_EXPIRES)

    try:
        # 上面两行代码可以写成一行
        # redis_store.setex(编号，有效期，内容)
        redis_store.setex("image_code_%s"%image_code_id,constance.IMAGE_CODE_REDIS_EXPIRES,text)
    except Exception as e:
        current_app.logger.error(e)
        # {"errno":"4001","errmsg":"保存图片验证码失败"}
        return jsonify(errno=RET.DBERR,errmsg="保存图片验证码失败")

    # 3.3 将生成的图片验证码图片回送到浏览器
    response = make_response(image_data)
    # 设置响应头，告诉浏览器，服务器本次回送的是图片
    response.headers["Content-Type"] = "image/png"
    # 4. 返回响应结果
    return response

from flask import request
# GET http://127.0.0.1:5000/api/v1.0/sms_codes/15033889442?image_code_id=xxx&image_code=yyy
@api.route("/sms_codes/<re(r'1[3-9]\d{9}'):mobile>")
def get_sms_code(mobile):
    # 1. 接收参数
    args = request.args # 获取查询字符串中的数据 ImmutableMultiDict([(image_code_id,xxx),(mage_code,yyy)])
    image_code_id = args.get("image_code_id")
    image_code = args.get("image_code")

    # 2.校验参数
    if not all([image_code_id,image_code]):
        # {"errno":"4103","errmsg":"参数不完整"}
        return jsonify(errno=RET.PARAMERR,errmsg="参数不完整")
    # 3. 执行业务逻辑
    try:
        # 3.1 从redis数据库中取出之前存储的图片验证码
        real_image_code = redis_store.get("image_code_%s"%image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR,errmsg="redis数据库异常")

    if real_image_code is None:
        # {"errno":4002,"errmsg":"图片验证码失效"}
        return jsonify(errno=RET.NODATA, errmsg="图片验证码失效")

    try:
        # 在这里删除图片验证码最合适，已经将本次使用的图片验证码取出来了，并且不为空
        redis_store.delete("image_code_%s"%image_code_id)
    except Exception as e:
        current_app.logger.error(e)

    # 将字节类型的数据解码成字符串
    real_image_code = real_image_code.decode("utf-8")
    # 将用户输入的图片验证码和从redis数据库中取出来的图片验证码进行对比
    if real_image_code.lower()!=image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码错误")

    try:
        # 判断用户在60秒之前是否发送过短信 get send_sms_code_15033889441
        send_flag = redis_store.get("send_sms_code_%s"%mobile)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if send_flag is not None:
            return jsonify(errno=RET.REQERR,errmsg="请求过于频繁，请60秒后重试！")
    # 如果在这里删除图片验证码，则用户可以多次尝试，直到验证码正确为止，有安全漏洞
    # 进行短信验证码处理
    try:
        # A:判断手机号是否存在
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
    else:
        # 写在else里面的代码在没有发生异常时执行
        if user is not None: #说明手机号存在
            # {"errno":"4003","errsmg":"手机号已经存在"}
            return jsonify(errno=RET.DATAEXIST,errmsg="手机号已经存在")
    # 如果在这里删除图片验证码，则用户可以不停的尝试，知道哪些手机号已经注册过了，有安全漏洞

    # B:如果手机号不存在说明可以注册，发送短信验证码
    # 生成真实的短信验证码
    sms_code = "%04d"%random.randint(0,9999)
    print("sms_code=%s",sms_code)
    # C:并保存真实的短信验证码到redis数据库中
    try:
        # setex sms_code_15033889441 300 562345
        redis_store.setex("sms_code_%s"%mobile,constance.SMS_CODE_REDIS_EXPIRES,sms_code)
        # setex send_sms_code_15033889441 60 1
        redis_store.setex("send_sms_code_%s"%mobile,constance.SEND_SMS_CODE_INTERVAL,1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="保存短信验证码异常")

    # 发送短信
    # 使用celery异步发送短信，delay()函数调用后立即返回
    result = send_sms.delay(mobile, [sms_code, int(constance.SMS_CODE_REDIS_EXPIRES / 60)], 1)
    print("result=",result,",type=",type(result))

    # result.get()默认是阻塞式的方法,如果想让在指定时间内如果还没有获取结果则解除阻塞，
    # 则可以使用timeout参数指定超时时间
    ret = result.get(timeout=10)
    print("ret=",ret)

    # 返回值，短信发送成功
    return jsonify(errno=RET.OK, errmsg="短信发送成功")


# from flask import request
# # GET http://127.0.0.1:5000/api/v1.0/sms_codes/15033889442?image_code_id=xxx&image_code=yyy
# @api.route("/sms_codes/<re(r'1[3-9]\d{9}'):mobile>")
# def get_sms_code(mobile):
#     # 1. 接收参数
#     args = request.args # 获取查询字符串中的数据 ImmutableMultiDict([(image_code_id,xxx),(mage_code,yyy)])
#     image_code_id = args.get("image_code_id")
#     image_code = args.get("image_code")
#
#     # 2.校验参数
#     if not all([image_code_id,image_code]):
#         # {"errno":"4103","errmsg":"参数不完整"}
#         return jsonify(errno=RET.PARAMERR,errmsg="参数不完整")
#     # 3. 执行业务逻辑
#     try:
#         # 3.1 从redis数据库中取出之前存储的图片验证码
#         real_image_code = redis_store.get("image_code_%s"%image_code_id)
#     except Exception as e:
#         current_app.logger.error(e)
#         return jsonify(errno=RET.THIRDERR,errmsg="redis数据库异常")
#
#     if real_image_code is None:
#         # {"errno":4002,"errmsg":"图片验证码失效"}
#         return jsonify(errno=RET.NODATA, errmsg="图片验证码失效")
#
#     try:
#         # 在这里删除图片验证码最合适，已经将本次使用的图片验证码取出来了，并且不为空
#         redis_store.delete("image_code_%s"%image_code_id)
#     except Exception as e:
#         current_app.logger.error(e)
#
#     # 将字节类型的数据解码成字符串
#     real_image_code = real_image_code.decode("utf-8")
#     # 将用户输入的图片验证码和从redis数据库中取出来的图片验证码进行对比
#     if real_image_code.lower()!=image_code.lower():
#         return jsonify(errno=RET.DATAERR, errmsg="图片验证码错误")
#
#     try:
#         # 判断用户在60秒之前是否发送过短信 get send_sms_code_15033889441
#         send_flag = redis_store.get("send_sms_code_%s"%mobile)
#     except Exception as e:
#         current_app.logger.error(e)
#     else:
#         if send_flag is not None:
#             return jsonify(errno=RET.REQERR,errmsg="请求过于频繁，请60秒后重试！")
#     # 如果在这里删除图片验证码，则用户可以多次尝试，直到验证码正确为止，有安全漏洞
#     # 进行短信验证码处理
#     try:
#         # A:判断手机号是否存在
#         user = User.query.filter_by(mobile=mobile).first()
#     except Exception as e:
#         current_app.logger.error(e)
#     else:
#         # 写在else里面的代码在没有发生异常时执行
#         if user is not None: #说明手机号存在
#             # {"errno":"4003","errsmg":"手机号已经存在"}
#             return jsonify(errno=RET.DATAEXIST,errmsg="手机号已经存在")
#     # 如果在这里删除图片验证码，则用户可以不停的尝试，知道哪些手机号已经注册过了，有安全漏洞
#
#     # B:如果手机号不存在说明可以注册，发送短信验证码
#     # 生成真实的短信验证码
#     sms_code = "%04d"%random.randint(0,9999)
#     print("sms_code=%s",sms_code)
#     # C:并保存真实的短信验证码到redis数据库中
#     try:
#         # setex sms_code_15033889441 300 562345
#         redis_store.setex("sms_code_%s"%mobile,constance.SMS_CODE_REDIS_EXPIRES,sms_code)
#         # setex send_sms_code_15033889441 60 1
#         redis_store.setex("send_sms_code_%s"%mobile,constance.SEND_SMS_CODE_INTERVAL,1)
#     except Exception as e:
#         current_app.logger.error(e)
#         return jsonify(errno=RET.DBERR,errmsg="保存短信验证码异常")
#
#     try:
#         # D:发送短信验证码到用户手机
#         cpp = CPP()
#         # 【云通讯】您的验证码是{1}，请于{2}分钟内正确输入
#         datas = [sms_code, int(constance.SMS_CODE_REDIS_EXPIRES/60)]
#         result = cpp.send_message(1,mobile,datas)
#     except Exception as e:
#         current_app.logger.error(e)
#         return jsonify(errno=RET.THIRDERR,errmsg="发送短信到%s手机出现异常"%mobile)
#     # E:根据短信验证码的发送状态返回不同的应答
#     if result == 0:
#         # 发送短信成功
#         return jsonify(errno=RET.OK, errmsg="发送短信到%s成功" % mobile)
#     else:
#         return jsonify(errno=RET.THIRDERR, errmsg="发送短信到%s手机失败" % mobile)
